package org.c41.reflect.signal;

import java.lang.reflect.Method;
import java.util.ArrayList;

import org.c41.system.ImpossibleException;
import org.c41.system.Tools;

@Tools
public final class SignalUtil {

	private SignalUtil(){
		throw new ImpossibleException();
	}

	public static void connectSlot(
			SignalObject source, String signal, Class<?>[] signalType, 
			Object dest, String slot, Class<?>[] slotType) {
		source.inner_connectSlot(signal, signalType, dest, slot, slotType);
	}
	
	public static void disconnectSlot(
			SignalObject source, String signal, Class<?>[] signalType, 
			Object dest, String slot, Class<?>[] slotType) {
		source.inner_disconnectSlot(signal, signalType, dest, slot, slotType);
	}

	public static void connectSignal(
			SignalObject source, String signal, Class<?>[] signalType, 
			SignalObject dest, String slot, Class<?>[] slotType) {
		source.inner_connectSignal(signal, signalType, dest, slot, slotType);
	}

	public static void disconnectSignal(
			SignalObject source, String signal, Class<?>[] signalType, 
			SignalObject dest, String slot, Class<?>[] slotType) {
		source.inner_disconnectSignal(signal, signalType, dest, slot, slotType);
	}
	
	public static SignalObject getSignalSource(){
		return SignalTable.findSignal();
	}
	
	public static Method[] getSignalMethods(Class<? extends SignalObject> cl){
		ArrayList<Method> result = new ArrayList<>();
		Class<?> p = cl;
		while(p!=SignalObject.class){
			Method[] methods = p.getDeclaredMethods();
			for(Method method:methods){
				Signal mark = method.getAnnotation(Signal.class);
				if(mark!=null){
					result.add(method);
				}
			}
			p = p.getSuperclass();
		}
		return result.toArray(new Method[result.size()]);
	}
	
	public static Method[] getSlotMethods(Class<?> cl){
		ArrayList<Method> result = new ArrayList<>();
		while(cl!=SignalObject.class){
			Method[] methods = cl.getDeclaredMethods();
			for(Method method:methods){
				Slot mark = method.getAnnotation(Slot.class);
				if(mark!=null){
					result.add(method);
				}
			}
			cl = cl.getSuperclass();
		}
		return result.toArray(new Method[result.size()]);
	}
	
}

class SignalTable{
	
	synchronized static void regesterSignal(SignalObject source){
		Thread thread = Thread.currentThread();
		if(source==null){
			deleteSignal(thread);
		}
		else{
			newSignal(thread, source);
		}
	}
	
	synchronized static SignalObject findSignal(){
		Thread thread = Thread.currentThread();
		for(ThreadSignalMap map:maps){
			if(map.thread==thread){
				return map.object;
			}
		}
		return null;
	}
	
	private static void newSignal(Thread thread, SignalObject source) {
		for(ThreadSignalMap map:maps){
			if(map.thread==thread){
				map.object=source;
				return;
			}
		}
		ThreadSignalMap map = new ThreadSignalMap();
		map.thread=thread;
		map.object=source;
		maps.add(map);
	}

	private static void deleteSignal(Thread thread) {
		for(ThreadSignalMap map:maps){
			if(map.thread==thread){
				maps.remove(map);
				return;
			}
		}
	}

	private static final ArrayList<ThreadSignalMap> maps = new ArrayList<>();
	
	private static class ThreadSignalMap{
		
		private Thread thread;
		private SignalObject object;
		
	}
	
}